﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Whoot
{
    public class EventDispatcherNode : MonoBehaviour
    {
        /// <summary>
        /// 消息回调委托
        /// </summary>
        /// <param name="data">消息内容</param>
        /// <returns>是否中断消息发送</returns>
        public delegate void EventListenerDele(EventData data);

        /// <summary>
        /// 用于保存在逻辑帧开始的时候需要更新的ListenerPack
        /// </summary>
        private readonly Queue _listenersToUpdate = new Queue();

        /// <summary>
        /// 所有的消息
        /// </summary>
        private readonly Dictionary<string, List<EventInfo>> _eventDic = new Dictionary<string, List<EventInfo>>();

        /// <summary>
        /// 用于在线程安全状态下防止对当前消息队列的同时读写
        /// </summary>
        private const string EvtQueueLock = "lock";

        private Queue _evtQueueNext = new Queue();

        /// <summary>
        /// 两个消息队列buffer
        /// </summary>
        private Queue _eventQueueNow = new Queue();


        /// <summary>
        /// 是否线程安全
        /// </summary>
        public bool threadSafe;

        /// <summary>
        /// 当前节点是不是有效
        /// </summary>
        public virtual bool IsValid => gameObject != null && enabled && gameObject.activeInHierarchy;

        /// <summary>
        /// 是否存在这个监听函数
        /// </summary>
        /// <param name="type">事件类型</param>
        /// <param name="listenerr">回调函数</param>
        /// <returns></returns>
        public bool HasListener(string type, EventListenerDele listener)
        {
            if (_eventDic.ContainsKey(type))
                foreach (var lin in _eventDic[type])
                    if (lin.Listener == listener)
                        return true;

            return false;
        }

        /// <summary>
        /// 挂接一个监听eventKey消息的消息监听器
        /// 监听器将于下一逻辑帧执行前挂接
        /// </summary>
        /// <param name="type">消息id</param>
        /// <param name="listener">回调函数</param>
        /// <param name="priority">优先级</param>
        /// <param name="dispatchOnce">是否只接受一次</param>
        public void AttachListener(int type, EventListenerDele listener, int priority = 0, bool dispatchOnce = false)
        {
            AttachListener(type.ToString(), listener, priority, dispatchOnce);
        }

        /// <summary>
        /// 挂接一个监听eventKey消息的消息监听器
        /// 监听器将于下一逻辑帧执行前挂接
        /// </summary>
        /// <param name="type">消息id</param>
        /// <param name="listener">回调函数</param>
        /// <param name="priority">优先级</param>
        /// <param name="dispatchOnce">是否只接受一次</param>
        public void AttachListener(string type, EventListenerDele listener, int priority = 0,
            bool dispatchOnce = false)
        {
            if (threadSafe)
                lock (_listenersToUpdate)
                {
                    _listenersToUpdate.Enqueue(
                        ListenerPack.Get(EventInfo.Get(type, listener, priority, dispatchOnce), type, true));
                }
            else
                _listenersToUpdate.Enqueue(ListenerPack.Get(EventInfo.Get(type, listener, priority, dispatchOnce),
                    type, true));
        }


        public void DetachListener(int type, EventListenerDele listener)
        {
            DetachListener(type.ToString(), listener);
        }

        /// <summary>
        /// 摘除一个监听eventKey消息的消息监听器
        /// 监听器将于下一逻辑帧执行前摘除
        /// </summary>
        /// <param name="listener"></param>
        /// <param name="eventKey"></param>
        public void DetachListener(string type, EventListenerDele listener)
        {
            if (threadSafe)
                lock (_listenersToUpdate)
                {
                    _listenersToUpdate.Enqueue(ListenerPack.Get(EventInfo.Get(type, listener), type, false));
                }
            else
                _listenersToUpdate.Enqueue(ListenerPack.Get(EventInfo.Get(type, listener), type, false));
        }

        /// <summary>
        /// 执行一次针对eventKey的监听器挂接
        /// </summary>
        /// <param name="type">事件类型</param>
        /// <param name="listener">回调函数</param>
        /// <param name="priority">优先级</param>
        /// <param name="dispatchOnce">是否只派发一次</param>
        private void AttachListenerNow(int type, EventListenerDele listener, int priority = 0,
            bool dispatchOnce = false)
        {
            AttachListenerNow(type.ToString(), listener, priority, dispatchOnce);
        }

        /// <summary>
        /// 执行一次针对eventKey的监听器挂接
        /// </summary>
        /// <param name="type">事件类型</param>
        /// <param name="listener">回调函数</param>
        /// <param name="priority">优先级</param>
        /// <param name="dispatchOnce">是否只派发一次</param>
        private void AttachListenerNow(string type, EventListenerDele listener, int priority = 0,
            bool dispatchOnce = false)
        {
            if (null == listener || string.IsNullOrEmpty(type)) return;
            if (!_eventDic.ContainsKey(type)) _eventDic.Add(type, new List<EventInfo>());

            if (HasListener(type, listener))
            {
                Debug.Log("LogicNode, AttachListenerNow: " + listener + " is already in list for event: " +
                          type);
                return;
            }

            var listenerList = _eventDic[type];

            var ev = EventInfo.Get(type, listener, priority, dispatchOnce);

            var pos = 0;
            var countListenerList = listenerList.Count;
            for (var n = 0; n < countListenerList; n++)
            {
                if (ev.Priority > listenerList[n].Priority) break;

                pos++;
            }

            listenerList.Insert(pos, ev);
        }

        /// <summary>
        /// 执行一次针对eventKey的监听器摘除
        /// </summary>
        /// <param name="type">事件类型</param>
        /// <param name="listener">监听函数</param>
        /// <param name="_priority">优先级</param>
        /// <param name="_dispatchOnce">是否只执行一次</param>
        private void DetachListenerNow(string type, EventListenerDele listener)
        {
            //listener == null is valid due to unexpected GameObject.Destroy
            if (string.IsNullOrEmpty(type) || listener == null)
            {
                Debug.LogError("回调函数或者 事件类型不能为空");
                return;
            }

            if (!_eventDic.ContainsKey(type))
                return;

            var listenerList = _eventDic[type];
            EventInfo ev = null;
            foreach (var ei in listenerList)
                if (ei.Listener == listener)
                    ev = ei;

            if (ev != null)
            {
                listenerList.Remove(ev);
                EventInfo.Return(ev);
            }
        }

        /// <summary>
        /// 更新消息监听器表格
        /// </summary>
        private void UpdateListenerMap()
        {
            if (threadSafe)
                lock (_listenersToUpdate)
                {
                    _UpdateListenerMap();
                }
            else
                _UpdateListenerMap();
        }

        /// <summary>
        /// 更新消息监听器表格的实现
        /// </summary>
        private void _UpdateListenerMap()
        {
            var countListenerPack = _listenersToUpdate.Count;
            while (countListenerPack != 0)
            {
                var pack = (ListenerPack) _listenersToUpdate.Dequeue();
                countListenerPack--;
                if (pack.AddOrRemove)
                    AttachListenerNow(pack.EventKey, pack.Listener.Listener, pack.Listener.Priority,
                        pack.Listener.DispatchOnce);
                else
                    DetachListenerNow(pack.EventKey, pack.Listener.Listener);

                ListenerPack.Return(pack);
            }
        }

        protected void Update()
        {
            UpdateListenerMap();
            DispatchEvent();
            OnUpdate();
        }

        protected virtual void OnUpdate()
        {
        }


        /// <summary>
        /// 消息对应的信息
        /// </summary>
        private class EventInfo
        {
            private static readonly Queue<EventInfo> Pools = new Queue<EventInfo>();

            /// <summary>
            /// 只发送一次
            /// </summary>
            public bool DispatchOnce;

            /// <summary>
            /// 事件类型
            /// </summary>
            public string EventType;

            /// <summary>
            /// 事件回调
            /// </summary>
            public EventListenerDele Listener;

            /// <summary>
            /// 事件派发的优先级
            /// </summary>
            public int Priority;

            private EventInfo(string type, EventListenerDele listener, int priority = 0, bool dispatchOnce = false)
            {
                Set(type, listener, priority, dispatchOnce);
            }

            public void Set(string type, EventListenerDele listener, int priority = 0, bool dispatchOnce = false)
            {
                Priority = priority;
                DispatchOnce = dispatchOnce;
                EventType = type;
                Listener = listener;
            }

            public void Clean()
            {
                Listener = null;
                DispatchOnce = false;
                EventType = string.Empty;
                Priority = 0;
            }

            public static EventInfo Get(string type, EventListenerDele listener, int priority = 0,
                bool dispatchOnce = false)
            {
                EventInfo info = null;
                if (Pools.Count > 0)
                {
                    info = Pools.Dequeue();
                    info.Set(type, listener, priority, dispatchOnce);
                }
                else
                {
                    info = new EventInfo(type, listener, priority, dispatchOnce);
                }

                return info;
            }

            public static void Return(EventInfo info)
            {
                info.Clean();
                Pools.Enqueue(info);
            }
        }

        /// <summary>
        /// 用于在每逻辑帧开始的时候维护消息监听器序列
        /// </summary>
        private class ListenerPack
        {
            private static readonly Queue<ListenerPack> ListenerPackPools = new Queue<ListenerPack>();
            public bool AddOrRemove;
            public string EventKey;
            public EventInfo Listener;

            public ListenerPack(EventInfo lis, string eKey, bool addOrRe)
            {
                Set(lis, eKey, addOrRe);
            }

            public void Set(EventInfo lis, string eKey, bool addOrRe)
            {
                Listener = lis;
                EventKey = eKey;
                AddOrRemove = addOrRe;
            }

            public void Clean()
            {
                Listener = null;
                EventKey = string.Empty;
                AddOrRemove = false;
            }

            public static ListenerPack Get(EventInfo info, string eKey, bool addOrRe)
            {
                ListenerPack pack = null;
                if (ListenerPackPools.Count > 0)
                {
                    pack = ListenerPackPools.Dequeue();
                    pack.Set(info, eKey, addOrRe);
                }
                else
                {
                    pack = new ListenerPack(info, eKey, addOrRe);
                }

                return pack;
            }

            public static void Return(ListenerPack info)
            {
                info.Clean();
                ListenerPackPools.Enqueue(info);
            }
        }

        #region 派发消息相关

        /// <summary>
        /// 发送一个默认的int类型值
        /// </summary>
        /// <param name="key"></param>
        public void QueueEvent(string key)
        {
            QueueEvent(new EventData(key));
        }

        /// <summary>
        /// 发送一个默认的int类型值
        /// </summary>
        /// <param name="key"></param>
        public void QueueEvent(int key)
        {
            QueueEvent(key.ToString());
        }

        /// <summary>
        /// 发送一个默认的int类型值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void QueueEvent<T>(int key, T value)
        {
            QueueEvent(key.ToString(), value);
        }

        public void QueueEvent<T>(string key, T value)
        {
            var data = new EventData<T>(key, value);
            QueueEvent(data);
        }

        /// <summary>
        /// 线程安全
        /// 抛出消息进入下一逻辑帧
        /// </summary>
        public void QueueEvent(EventData data)
        {
            if (threadSafe)
                lock (EvtQueueLock)
                {
                    _evtQueueNext.Enqueue(data);
                }
            else
                QueueEventNow(data);
        }

        /// <summary>
        /// 只能在主线程调用 立即派发消息
        /// </summary>
        /// <param name="key">要发送的消息id</param>
        public void QueueEventNow(int key)
        {
            QueueEventNow(new EventData(key.ToString()));
        }

        /// <summary>
        /// 只能在主线程调用 立即派发消息
        /// </summary>
        /// <param name="key">要发送的消息id</param>
        public void QueueEventNow(string key)
        {
            QueueEventNow(new EventData(key));
        }

        /// <summary>
        /// 只能在主线程调用 立即派发消息
        /// </summary>
        /// <typeparam name="T">消息传入的参数</typeparam>
        /// <param name="key">要发送的消息id</param>
        /// <param name="value">要发的消息内容</param>
        public void QueueEventNow<T>(string key, T value)
        {
            QueueEventNow(new EventData<T>(key, value));
        }

        /// <summary>
        /// 只能在主线程调用 立即发送
        /// </summary>
        /// <param name="data"></param>
        public void QueueEventNow(EventData data)
        {
            if (threadSafe)
                lock (EvtQueueLock)
                {
                    TriggerEvent(data);
                }
            else
                TriggerEvent(data);
        }


        /// <summary>
        /// 派发消息
        /// </summary>
        private void DispatchEvent()
        {
            if (threadSafe)
                _DispatchEventLock();
            else
                _DispatchEventNoLock();
        }

        private void _DispatchEventLock()
        {
            if (!IsValid) return;
            lock (EvtQueueLock)
            {
                //发布自己的event
                var countEventQueue = _eventQueueNow.Count;
                while (countEventQueue > 0)
                {
                    var evt = _eventQueueNow.Dequeue() as EventData;
                    countEventQueue--;
                    if (evt != null) TriggerEvent(evt);
                }
            }

            lock (EvtQueueLock)
            {
                SwapEventQueue();
            }
        }

        private void _DispatchEventNoLock()
        {
            if (!IsValid) return;

            var countEventQueue = _eventQueueNow.Count;
            while (countEventQueue > 0)
            {
                var evt = _eventQueueNow.Dequeue() as EventData;
                countEventQueue--;
                if (evt != null) TriggerEvent(evt);
            }

            //交换event缓冲//
            SwapEventQueue();
        }

        /// <summary>
        /// 交换消息队列
        /// </summary>
        private void SwapEventQueue()
        {
            var temp = _eventQueueNow;
            _eventQueueNow = _evtQueueNext;
            _evtQueueNext = temp;
        }

        /// <summary>
        /// 将消息派发给下携的消息监听器
        /// </summary>
        /// <param name="key"></param>
        /// <param name="param1"></param>
        /// <param name="param2"></param>
        /// <returns></returns>
        private void TriggerEvent(EventData data)
        {
            if (!_eventDic.ContainsKey(data.Name)) return;

            var reList = new List<EventInfo>();

            var listenerList = _eventDic[data.Name];
            var countListenerList = listenerList.Count;
            for (var n = 0; n < countListenerList; n++)
            {
                if (listenerList[n].DispatchOnce) reList.Add(listenerList[n]);

                if (listenerList[n].Listener != null) listenerList[n].Listener(data);

                if (data.IsBreak) break;
            }

            for (var n = reList.Count - 1; n >= 0; n--) DetachListenerNow(reList[n].EventType, reList[n].Listener);
        }

        #endregion
    }

    public class EventListenerMgr
    {
        private readonly List<EventListenerData> _listener = new List<EventListenerData>();

        public void AddListener(EventDispatcherNode dis, EventDispatcherNode.EventListenerDele dele, int type
            , int priority = 0, bool dispatchOnce = false)
        {
            AddListener(dis, dele, type.ToString(), priority, dispatchOnce);
        }

        public void AddListener(EventDispatcherNode dis, EventDispatcherNode.EventListenerDele dele, string type
            , int priority = 0, bool dispatchOnce = false)
        {
            if (!dis.HasListener(type, dele))
            {
                var data = new EventListenerData(dis, dele, type);
                _listener.Add(data);
                dis.AttachListener(type, dele, priority, dispatchOnce);
            }
            else
            {
                Debug.LogWarning("添加消息失败重复添加消息id=" + type);
            }
        }

        public void DetachListener(EventDispatcherNode dis, EventDispatcherNode.EventListenerDele dele, string type)
        {
            if (dis.HasListener(type, dele))
            {
                dis.DetachListener(type, dele);
                for (var i = 0; i < _listener.Count; i++)
                {
                    var data = _listener[i];
                    if (data.Dis == dis && data.Type == type && data.Dele == dele)
                    {
                        _listener.RemoveAt(i);
                        i--;
                    }
                }
            }
        }

        public void DetachListenerAll()
        {
            foreach (var data in _listener) data.DetachListener();

            _listener.Clear();
        }

        public class EventListenerData
        {
            public EventDispatcherNode.EventListenerDele Dele;
            public EventDispatcherNode Dis;
            public bool DispatchOnce;
            public int Priority;
            public string Type;

            public EventListenerData(EventDispatcherNode dis,
                EventDispatcherNode.EventListenerDele dele, string type,
                int priority = 0, bool dispatchOnce = false)
            {
                Dis = dis;
                Dele = dele;
                Type = type;
                Priority = priority;
                DispatchOnce = dispatchOnce;
            }

            public void DetachListener()
            {
                Dis.DetachListener(Type, Dele);
            }
        }
    }
}